package com.how.warlock.engine;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.util.HashMap;
import java.util.Hashtable;


import javax.imageio.ImageIO;

import org.lwjgl.BufferChecks;
import org.lwjgl.BufferUtils;

import static org.lwjgl.opengl.GL11.*;

public class TextureLoader {
	private HashMap<String, Texture> table;
	private HashMap<String, Integer> amountTable;
	
	private ColorModel glAlphaColorModel;
	private ColorModel glColorModel;
	
	private IntBuffer textureIDBuffer = BufferUtils.createIntBuffer(1);
	
	public TextureLoader(){
		glAlphaColorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB),
													new int[] {8,8,8,8},
													true,
													false,
													ComponentColorModel.TRANSLUCENT,
													DataBuffer.TYPE_BYTE);
		glColorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB),
												new int[] {8,8,8,0},
												false,
												false,
												ComponentColorModel.OPAQUE,
												DataBuffer.TYPE_BYTE);
		table = new HashMap<String, Texture>();
		amountTable = new HashMap<String, Integer>();
	}
	
	private int createTextureID(){
		glGenTextures(textureIDBuffer);
		return textureIDBuffer.get(0);
	}
	
	public Texture getTexture(String resourceName) throws IOException{
		Texture tex = table.get(resourceName);
		
		if(tex != null){
			return tex;
		}
		
		tex = getTexture(resourceName,
							GL_TEXTURE_2D,
							GL_RGBA,
							GL_LINEAR,
							GL_LINEAR);
		
		table.put(resourceName, tex);
		
		return tex;
	}
	
	public Texture[] getTexture(String resourceName, int width) throws IOException{
		return getTexture(resourceName, width, width);
	}
	
	public Texture[] getTexture(String resourceName, int width, int height) throws IOException{		
		BufferedImage images[] = ImageLoader.loadImage(resourceName, width, height); 
		
		Texture[] textures = new Texture[images.length];
		
		amountTable.put(resourceName, images.length);
		for(int i=0; i < images.length; i++){
			textures[i] = getTexture(images[i],
										GL_TEXTURE_2D,
										GL_RGBA,
										GL_LINEAR,
										GL_LINEAR);
			table.put(resourceName + "i" + i, textures[i]);
		}	
		
		return textures;
	}
	
	public Texture getTexture(BufferedImage image,
								int target,
								int dstPixelFormat,
								int minFilter,
								int magFilter) throws IOException{
		int srcPixelFormat;
		
		int textureID = createTextureID();
		Texture texture = new Texture(target, textureID);
		
		glBindTexture(target,textureID);
		
		BufferedImage bufferedImage = image;
		
		
		
		texture.setWidth(bufferedImage.getWidth());
		texture.setHeight(bufferedImage.getHeight());
		
		if(bufferedImage.getColorModel().hasAlpha()){
		srcPixelFormat = GL_RGBA;
		} else{
		srcPixelFormat = GL_RGB;
		}
		
		
		ByteBuffer textureBuffer = convertImageData(bufferedImage, texture);
		
		
		if (target == GL_TEXTURE_2D){
		glTexParameteri(target, GL_TEXTURE_MIN_FILTER, minFilter);
		glTexParameteri(target, GL_TEXTURE_MAG_FILTER, magFilter);
		}
		
		glTexImage2D(target,
			0,
			dstPixelFormat,
			get2Fold(bufferedImage.getWidth()),
			get2Fold(bufferedImage.getHeight()),
			0,
			srcPixelFormat,
			GL_UNSIGNED_BYTE,
			textureBuffer );
		
		texture.setWidth();
		texture.setHeight();
		
		return texture;
	}
	
	
	public Texture getTexture(String resourceName,
								int target,
								int dstPixelFormat,
								int minFilter,
								int magFilter) throws IOException{
		int srcPixelFormat;
		
		int textureID = createTextureID();
		Texture texture = new Texture(target, textureID);
		
		glBindTexture(target,textureID);
		
		BufferedImage bufferedImage = ImageLoader.loadImage(resourceName);
		
		texture.setWidth(bufferedImage.getWidth());
		texture.setHeight(bufferedImage.getHeight());
		
		if(bufferedImage.getColorModel().hasAlpha()){
			srcPixelFormat = GL_RGBA;
		} else{
			srcPixelFormat = GL_RGB;
		}
		
		
		ByteBuffer textureBuffer = convertImageData(bufferedImage, texture);
		
		
		if (target == GL_TEXTURE_2D){
			glTexParameteri(target, GL_TEXTURE_MIN_FILTER, minFilter);
			glTexParameteri(target, GL_TEXTURE_MAG_FILTER, magFilter);
		}
		
		glTexImage2D(target,
						0,
						dstPixelFormat,
						get2Fold(bufferedImage.getWidth()),
						get2Fold(bufferedImage.getHeight()),
						0,
						srcPixelFormat,
						GL_UNSIGNED_BYTE,
						textureBuffer );
		
		texture.setWidth();
		texture.setHeight();
		
		return texture;
	}
	
	private static int get2Fold(int fold) {
		int ret = 2;
		while(ret < fold){
			ret *= 2;
		}
		return ret;	
	}
	
	private ByteBuffer convertImageData(BufferedImage bufferedImage, Texture texture){
		ByteBuffer imageBuffer;
		WritableRaster raster;
		BufferedImage texImage;
		
		int texWidth = get2Fold(bufferedImage.getWidth());
		int texHeight = get2Fold(bufferedImage.getHeight());
		
		texture.setTextureWidth(texWidth);
		texture.setTextureHeight(texHeight);
		
		if(bufferedImage.getColorModel().hasAlpha()){
			raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, texWidth, texHeight, 4, null);
			texImage = new BufferedImage(glAlphaColorModel,raster,false,new Hashtable());
		} else {
			raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, texWidth, texHeight, 3, null);
			texImage = new BufferedImage(glColorModel,raster,false,new Hashtable());
		}
		
		Graphics g = texImage.getGraphics();
		g.setColor(new Color(0f,0f,0f,0f));
		g.fillRect(0, 0, texWidth, texHeight);
		g.drawImage(bufferedImage,0,0,null);
		
		byte[] data = ((DataBufferByte) texImage.getRaster().getDataBuffer()).getData();
		
		imageBuffer = ByteBuffer.allocateDirect(data.length);
		imageBuffer.order(ByteOrder.nativeOrder());
		imageBuffer.put(data,0,data.length);
		imageBuffer.flip();
		
		return imageBuffer;
	}
}
